﻿/*
 * TIFFImageFileDirectory
 * 
 * Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006 Marco Schmidt.
 * All rights reserved.
 * 
 * C# translation and update 
 * Copyright (c) Adam Sida, asida@centrum.cz
 * 
 * All rights reserved.
 */
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;


namespace Petra.Gis.ImagingServices.Tiff
{

  /// <summary>
  /// ImageFileDirectory is base structure in tiff file that holds all tags and provides
  /// some basic api and basic computations to tags
  /// use GetTag function to get proper tag data as tag object
  /// </summary>
  public partial class ImageFileDirectory
  {

    private Dictionary<TagType, TagBase> _tags;


    private String dateTime;  //todo: convert to DateTime value
    //private PhotometricType photometricInterpretation;
    private int _nextTablePtr;



    #region constructors

    /// <summary>
	  /// Initializes only tag array
    /// </summary>
    private ImageFileDirectory()
    {
      _tags = new Dictionary<TagType, TagBase>();
    }



    /// <summary>
    /// this is constructor that creates basic definition for tiff file structure for single ifd
    /// </summary>
    /// <param name="ifdTextDefinition"></param>
    public ImageFileDirectory(string ifdTextDefinition)
      :this()
    {
      throw new NotImplementedException("sorry not possible now");
    }


    ///<summary>
    /// Reads a  TIFF ImageFileDirectory from current position in reader
    ///</summary>
    /// <param name="tiffReader">source data reader</param>
    protected ImageFileDirectory(TiffReader tiffReader)
      : this()
    {
      short numTags = tiffReader.ReadInt16();
      if (numTags < 0)
      {
        throw new InvalidFileStructureException("Number of tags in IFD " +
          "smaller than 1 @" + tiffReader.BaseStream.Position + ": " + numTags);
      }

      // save current offset
      long oldOffset = tiffReader.BaseStream.Position;

      for (int i = 0; i < numTags; i++)
      {
        // process tag
        //Tag tag = new Tag(tiffReader);
        TagBase tag = TagBase.Create(tiffReader);
        if (tag != null)
        {
          _tags.Add(tag.Id, tag);
          //this.append(tag);
        }

        // restore position
        // offset based fields will
        // read all data and will change position
        // each Ifd tag is 12 bytes
        oldOffset += 12;
        tiffReader.BaseStream.Seek(oldOffset, SeekOrigin.Begin);
      }

      _nextTablePtr = tiffReader.ReadInt32();
      validateSpecifisTags();
      checkContent();

    }

    #endregion




    #region public helper functions

    public TagBase GetTag(TagType tId)
    {
      return _tags[tId];
    }

    #endregion of public helper functions







    //internal RGBPalette Palette
    //{
    //  get { return palette; }
    //}




    public void validateSpecifisTags()
    {
      TagBase tag;
      foreach (KeyValuePair<TagType, TagBase> tagEntry in _tags)// (index < tags.Length)
      {
        tag = tagEntry.Value;
        switch (tag.Id)
        {
          case (TagType.ARTIST):
          case (TagType.HOSTCOMPUTER):
          case (TagType.IMAGEDESCRIPTION):
          case (TagType.SOFTWARE):
            //skip
            break;
          case (TagType.BITSPERSAMPLE):
            {
              checkBitsPerSample((Tag)tag);
              break;
            }
          case (TagType.COLORMAP):
            {
              prepareColorMap((UShortArrayTag)tag);
              break;
            }
          case (TagType.COMPRESSION):
            {
              checkCompression((Tag)tag);
              break;
            }
          case (TagType.DATETIME):
            {
              prepareDateTime((Tag)tag);
              break;
            }
          case (TagType.IMAGEWIDTH):
            {
              if (tag.Count != 1 || !tag.isInt())
              {
                throw new InvalidFileStructureException("Expected " +
                  "single byte/short/long value for image width " +
                  "(count=" + tag.Count + ", type=" + tag.Type + ").");
              }
              break;
            }
          case (TagType.IMAGELENGTH):
            {
              if (tag.Count != 1 || !tag.isInt())
              {
                throw new InvalidFileStructureException("Expected " +
                  "single byte/short/long value for image height " +
                  "(count=" + tag.Count + ", type=" + tag.Type + ").");
              }
              //height = tag.Offset;
              break;
            }
          case (TagType.MAKE):
            {
              break;
            }
          case (TagType.MODEL):
            {
              break;
            }
          case (TagType.ORIENTATION):
            {
              if (tag.Count != 1 || !tag.isInt())
              {
                throw new InvalidFileStructureException("Expected " +
                  "single byte/short/long value for image height " +
                  "(count=" + tag.Count + ", type=" + tag.Type + ").");
              }
              break;
            }
          case (TagType.PHOTOMETRIC):
            {
              preparePhotometric((Tag)tag);
              break;
            }
          case (TagType.RESOLUTIONUNIT):
            {
              if (tag.Count != 1 || !tag.isInt())
              {
                throw new InvalidFileStructureException("Expected " +
                  "single byte/short/long value for planar configuration.");
              }
              break;
            }
          case (TagType.XRESOLUTION):
            {
              if (tag.Count != 1 || tag.Type != TagDataType.RATIONAL)
              {
                throw new InvalidFileStructureException("Expected " +
                  "single byte/short/long value for planar configuration.");
              }
              break;
            }
          case (TagType.YRESOLUTION):
            {
              if (tag.Count != 1 || tag.Type != TagDataType.RATIONAL)
              {
                throw new InvalidFileStructureException("Expected " +
                  "single byte/short/long value for planar configuration.");
              }
              break;
            }
          case (TagType.PLANARCONFIG):
            {
              if (tag.Count != 1 || !tag.isInt())
              {
                throw new InvalidFileStructureException("Expected " +
                  "single byte/short/long value for planar configuration.");
              }
              break;
            }
          case (TagType.ROWSPERSTRIP):
            {
              if (tag.Count != 1 || !tag.isInt())
              {
                throw new InvalidFileStructureException("Expected " +
                  "single byte/short/long value for image height.");
              }
              break;
            }
          case (TagType.SAMPLESPERPIXEL):
            {
              if (tag.Count != 1 || !tag.isInt())
              {
                throw new InvalidFileStructureException("Expected " +
                  "single byte/short/long value for samples per pixel.");
              }
              break;
            }
          case (TagType.STRIPBYTECOUNTS):
            {
              checkStripByteCounts((UIntArrayTag)tag);
              break;
            }
          case (TagType.STRIPOFFSETS):
            {
              checkStripOffsets((UIntArrayTag)tag);
              break;
            }
          case (TagType.T4OPTIONS):
            {
              if (tag.Count != 1 || !tag.isInt())
              {
                throw new InvalidFileStructureException("Expected " +
                  "single byte/short/long value for T4 Options.");
              }
              break;
            }
          case (TagType.T6OPTIONS):
            {
              if (tag.Count != 1 || !tag.isInt())
              {
                throw new InvalidFileStructureException("Expected " +
                  "single byte/short/long value for T6 Options.");
              }
              break;
            }
          case (TagType.TILELENGTH):
            {
              checkTileHeight((Tag)tag);
              break;
            }
          case (TagType.TILEWIDTH):
            {
              checkTileWidth((Tag)tag);
              break;
            }
          case (TagType.TILEOFFSETS):
            {
              checkTileOffsets((UIntArrayTag)tag);
              break;
            }

        }
      }
    }



    private void checkContent()
    {
      //if (planarConfiguration == PlanarConfigurationType.UNKNOWN)
      //{
      //  planarConfiguration = PlanarConfigurationType.CONTIG;
      //}

      //if (resolutionUnit == 2 && resolutionX > 0.0 && resolutionY > 0.0)
      //{
      //  dpiX = (int)resolutionX;
      //  dpiY = (int)resolutionY;
      //}

      //if (isStriped())
      //{
      //  tileWidth = width;
      //  if (numStrips == 1 && rowsPerStrip == -1)
      //  {
      //    rowsPerStrip = height;
      //  }
      //  tileHeight = rowsPerStrip;
      //}

      //if (width < 1)
      //{
      //  throw new InvalidFileStructureException("No valid width available.");
      //}
      //if (height < 1)
      //{
      //  throw new InvalidFileStructureException("No valid width available.");
      //}
      //if (stripOffsets != null)
      //{
      //  pixelsPerRow = width;
      //}
      //else
      //  if (tileOffsets != null)
      //  {
      //    pixelsPerRow = tileWidth;
      //  }
      //if (rowsPerStrip == -1 && stripOffsets != null && stripOffsets.Count == 1)
      //{
      //  rowsPerStrip = height;
      //}
    }




    //public bool isGrayscale()
    //{
    //  return this.BitsPerPixel > 1 &&
    //    (photometricInterpretation == PhotometricType.BLACK_IS_ZERO ||
    //     photometricInterpretation == PhotometricType.WHITE_IS_ZERO);
    //}








    ///**
    // * Sets the time zone to be used when trying to interpret dates
    // * found in a {@link #TAG_DATE_TIME} tag.
    // * Example call: 
    // * <code>setTimeZone(TimeZone.getTimeZone("America/New_York");</code>.
    // * @param tz TimeZone object
    // */
    //public void setTimeZone(TimeZone tz)
    //{
    //  timeZone = tz;
    //}



    public int NextTableOffset
    {
      get { return _nextTablePtr; }
    }




    //public ImageFileDirectory Clone()
    //{
    //  return new ImageFileDirectory();
    //}




    public static ImageFileDirectory Create(TiffReader tiffReader)
    { 

      //do basic validation...
      short numTags = tiffReader.ReadInt16();
      if (numTags < 0)
      {
        throw new InvalidFileStructureException("Number of tags in IFD " +
          "smaller than 1 @" + tiffReader.BaseStream.Position + ": " + numTags);
      }

      // seek back
      tiffReader.BaseStream.Seek(-2, SeekOrigin.Current);

      return new ImageFileDirectory(tiffReader);
    }

  }

}
